package com.chaosj.xml;

import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.json.JSONUtil;
import com.dcits.webservice.client.S2001100000101.S2001100000101Request;
import com.dcits.webservice.client.S2001100000101.S2001100000101Response;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @className: InterfaceAnalysisTool
 * @description:
 * @author: caoyangjie
 * @date: 2022/5/11
 **/
public class InterfaceAnalysisTool {
    private static Logger logger = LoggerFactory.getLogger(InterfaceAnalysisTool.class);

    public static void main(String[] args) {
        try {
            Path javaSourcePath = new ClassPathResource("BalanceMngFacade.java").getFile().toPath();
            List<String> sources = Files.readAllLines(javaSourcePath, Charset.forName("UTF-8"));
            Long modifiedTime = javaSourcePath.toFile().lastModified();
            RpcInterfaceEntry interfaceEntry = new RpcInterfaceEntry();
            interfaceEntry.setVersion(modifiedTime+"");
            interfaceEntry.setPackageName(packageName(sources));
            interfaceEntry.setImports(importMaps(sources));
            interfaceEntry.setInterfaceName(interfaceName(sources));
            interfaceEntry.setFunctions(getFunctions(interfaceEntry.getPackageName(),sources,interfaceEntry.getImports()));
            interfaceEntry.writerString(new ClassPathResource("/").getFile());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void json(){
        S2001100000101Request request = new S2001100000101Request();
        S2001100000101Request.SysHead sysHead = new S2001100000101Request.SysHead();
        sysHead.setServiceCode("");
        sysHead.setAplGnd("");
        sysHead.setServiceScene("");
        request.setSysHead(sysHead);
        S2001100000101Request.AppHead appHead = new S2001100000101Request.AppHead();
        appHead.setAplFamAdr("");
        request.setAppHead(appHead);
        S2001100000101Request.Body body = new S2001100000101Request.Body();
        body.setTranDate("");
        body.setAplGnd("");
        request.setBody(body);
        S2001100000101Response response = new S2001100000101Response();
        S2001100000101Response.SysHead sysHead1 = new S2001100000101Response.SysHead();
        sysHead1.setServiceScene("");
        sysHead1.setServiceScene("");
        response.setSysHead(sysHead1);
        S2001100000101Response.AppHead appHead1 = new S2001100000101Response.AppHead();
        List<S2001100000101Response.AppHead.ApprTellerArray> list = new ArrayList<>();
        S2001100000101Response.AppHead.ApprTellerArray arr = new S2001100000101Response.AppHead.ApprTellerArray();
        S2001100000101Response.AppHead.ApprTellerArray.AhrTlrInfArray_array array_array = new S2001100000101Response.AppHead.ApprTellerArray.AhrTlrInfArray_array();
        array_array.setCrnPyCoAcctNo("");
        arr.setCrnPyCoRgstNo("");
        arr.setAhrTlrInfArray_array(Arrays.asList(array_array));
        list.add(arr);
        appHead1.setApprTellerArray(list);
        appHead1.setCardCntrSeqNo("");
        response.setAppHead(appHead1);
        S2001100000101Response.Body body1 = new S2001100000101Response.Body();
        body1.setCrnPyCoNm("");
        body1.setCrnPyCoPySt("");
        body1.setCtrYr("");
        S2001100000101Response.Body.ApprTellerArray bodyArr = new S2001100000101Response.Body.ApprTellerArray();
        bodyArr.setCrScr("");
        S2001100000101Response.Body.BtchCstDealArray btchArr = new S2001100000101Response.Body.BtchCstDealArray();
        btchArr.setNum("");
        btchArr.setOrdrAdjRmk("");
        body1.setApprTellerArray(Arrays.asList(bodyArr));
        body1.setBtchCstDealArray(Arrays.asList(btchArr));
        Map<String,Object> reqRspEntity = new HashMap<>();
        reqRspEntity.put("request", request);
        reqRspEntity.put("response", response);
        System.out.println(JSONUtil.toJsonStr(reqRspEntity));
    }

    private static String packageName(List<String> sources){
        return sources.stream().filter(line->line.trim().startsWith("package")).findAny().orElseThrow(()->new RuntimeException("Java类文件存在问题，无法找到类所在包路径")).replaceFirst("package ","").replaceFirst(";","");
    }

    private static Map<String,String> importMaps(List<String> sources){
        Map<String,String> importMaps = new HashMap<>();
        sources.stream().filter(line->line.trim().startsWith("import")).forEach(line-> {
            List<String> names = Splitter.on(".").trimResults().splitToList(line.replace("import ",""));
            importMaps.put(names.get(names.size()-1).replace(";",""), line.replaceFirst("import ","").replace(";",""));
        });
        return importMaps;
    }

    private static String interfaceName(List<String> sources){
        String interfaceLine = sources.stream().filter(line->line.trim().startsWith("public interface")).findFirst().orElseThrow(()->new RuntimeException("Java类文件存在问题,无法找到接口名称"));
        List<String> interfaces = Splitter.on(" ").trimResults().omitEmptyStrings().splitToList(interfaceLine.replace("{",""));
        return interfaces.get(2);
    }

    private static List<FunctionSignEntry> getFunctions(String packageName, List<String> sources, Map<String, String> imports){
        String baseType = "String,Integer,Short,Double,Float,Long,Char,void,Object";
        Splitter.on(",").splitToList(baseType).forEach(base->{
            imports.put(base,String.format("java.lang.%s",base));
        });
        List<FunctionSignEntry> functions = new ArrayList<>();
        sources.stream().filter(line-> isaBoolean(line)).forEach(functionLine->{
            int indexLeft = functionLine.indexOf("(");
            int indexRight = functionLine.indexOf(")");
            String nameAndResult = functionLine.substring(0,indexLeft);
            String argType = functionLine.substring(indexLeft+1,indexRight);
            List<String> nameResult = Splitter.on(" ").trimResults().omitEmptyStrings().splitToList(nameAndResult);
            String functionName = nameResult.get(1);
            String resultType = imports.getOrDefault(nameResult.get(0),packageName+"."+nameResult.get(0));
            Map<String,String> argTypes = Splitter.on(", ").withKeyValueSeparator(" ").split(argType);

            FunctionSignEntry fse = new FunctionSignEntry();
            fse.setFunctionName(functionName);
            fse.setResultType(resultType);
            String[] argsTypes = argTypes.keySet().stream().map(clazz->imports.getOrDefault(clazz,packageName+"."+clazz)).collect(Collectors.toList()).toArray(new String[0]);
            fse.setArgTypes(argsTypes);

            functions.add(fse);
        });
        return functions;
    }

    private static boolean isaBoolean(String line) {
        return line.endsWith(";") && !line.startsWith("/*") && !line.startsWith("*") && !line.contains("package ")
               && !line.contains("import ") && !line.contains("interface");
    }

    static class RpcInterfaceEntry{
        private String version;
        private String packageName;
        private String interfaceName;
        private Map<String,String> imports;
        private List<FunctionSignEntry> functions;

        public String getVersion() {
            return version;
        }

        public void setVersion(String version) {
            this.version = version;
        }

        public String getPackageName() {
            return packageName;
        }

        public void setPackageName(String packageName) {
            this.packageName = packageName;
        }

        public String getInterfaceName() {
            return interfaceName;
        }

        public void setInterfaceName(String interfaceName) {
            this.interfaceName = interfaceName;
        }

        public Map<String, String> getImports() {
            return imports;
        }

        public void setImports(Map<String, String> imports) {
            this.imports = imports;
        }

        public List<FunctionSignEntry> getFunctions() {
            return functions;
        }

        public void setFunctions(List<FunctionSignEntry> functions) {
            this.functions = functions;
        }

        public void writerString(File writeFile){
            if(writeFile.exists() && writeFile.isDirectory() ){
                writeFile = new File(writeFile.getAbsolutePath()+File.separator+interfaceName+".xml");
            }
            if( !writeFile.exists() ){
                try {
                    writeFile.createNewFile();
                } catch (IOException e) {
                }
            }
            StringBuffer buffer = new StringBuffer();
            String rootName = interfaceName;
            buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
            buffer.append(String.format("<%s version=\"%s\">\n", rootName, version ));
            buffer.append(String.format("    <package>%s</package>\n", packageName));
            buffer.append(String.format("    <interface>%s</interface>\n", interfaceName));
            buffer.append("    <functions>\n");
            functions.stream().forEach(function->{
                buffer.append(String.format("        <function name=\"%s\">\n", function.getFunctionName()));
                buffer.append(String.format("            <in>%s</in>\n", Joiner.on(",").join(function.getArgTypes())));
                buffer.append(String.format("            <out>%s</out>\n", function.getResultType()));
                buffer.append("        </function>\n");
            });
            buffer.append("    </functions>\n");
            buffer.append(String.format("</%s>", rootName));
            try {
                Files.write(writeFile.toPath(),buffer.toString().getBytes(), StandardOpenOption.WRITE);
            } catch (IOException e) {
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * 方法签名实体
     */
    static class FunctionSignEntry {
        private String functionName;
        private String[] argTypes;
        private String resultType;

        public String getFunctionName() {
            return functionName;
        }

        public void setFunctionName(String functionName) {
            this.functionName = functionName;
        }

        public String[] getArgTypes() {
            return argTypes;
        }

        public void setArgTypes(String[] argTypes) {
            this.argTypes = argTypes;
        }

        public String getResultType() {
            return resultType;
        }

        public void setResultType(String resultType) {
            this.resultType = resultType;
        }
    }
}
